package com.vcc.boss.core.component;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.vcc.boss.core.config.UsdtConfig;
import com.vcc.channel.base.model.request.CardCancelDto;
import com.vcc.channel.base.model.response.PageClearingVo;
import com.vcc.channel.base.model.response.PageVo;
import com.vcc.channel.base.service.ChannelBaseService;
import com.vcc.channel.service.service.LlService;
import com.vcc.core.constant.CardStatusEnum;
import com.vcc.core.model.usdt.TokenTransfer;
import com.vcc.core.model.usdt.TronTransactionResponse;
import com.vcc.core.util.HttpUtil;
import com.vcc.core.util.RedisCustomizeUtils;
import com.vcc.data.entity.*;
import com.vcc.service.boss.request.rechargeLog.AuditOrderDto;
import com.vcc.service.service.*;
import jakarta.annotation.Resource;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

@Log4j2
@Component
public class TaskComponent {

    @Resource
    private UsdtConfig usdtConfig;
    @Resource
    private ClientParamService clientParamService;
    @Resource
    private UsdtRechargeService usdtRechargeService;
    @Resource
    private RechargeLogService rechargeLogService;
    @Resource
    private WalletDetailService walletDetailService;
    @Resource
    private WalletService walletService;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private LlService llService;
    @Resource
    private UserCardService userCardService;
    @Resource
    private TransactionService transactionService;
    @Resource
    private WebhookSourceMessageService webhookSourceMessageService;
    @Resource
    private CardTypeService cardTypeService;
    @Resource
    private UserCardWalletService userCardWalletService;
    @Resource
    private UserStatisticsService userStatisticsService;
    @Resource
    private CardOrderService cardOrderService;
    @Resource
    private WithdrawService withdrawService;
    @Resource
    private UserService userService;
    @Value("${spring.profiles.active:dev}")
    private String activeProfile;
    @Resource
    private RiskService riskService;
    @Resource
    private ApplicationContext applicationContext;
    @Resource
    private RiskUserCardLogService riskUserCardLogService;
    @Autowired
    private RiskRuleService riskRuleService;


    //一分钟一次
    @Scheduled(cron = "0 0/1 * * * ?")
    public void checkUsdtRechargeOrder() {
        if (!RedisCustomizeUtils.setIfAbsent("check:usdt")) {
            log.info("USDT充值订单检查任务正在运行，跳过本次检查");
        }
        if (activeProfile.equals("dev")) {
            log.info("当前环境为开发环境，跳过USDT充值订单检查任务");
            RedisCustomizeUtils.removeKey("check:usdt");
            return;
        }
        log.info("开始检查USDT充值订单状态");
        try {
            DateTime endDate = new DateTime();
            DateTime startDate = DateUtil.offsetMinute(endDate, -30);
            List<UsdtRecharge> usdtRecharges = usdtRechargeService.listByStatus(2);
            log.info("开始检查USDT充值订单状态,当前时间段内等待订单数 {}", usdtRecharges.size());
            if (usdtRecharges.isEmpty()) {
                log.info("没有需要检查的USDT充值订单");
                return;
            }
            Map<String, TronTransactionResponse> transferMap = MapUtil.newHashMap();
            for (UsdtRecharge usdtRecharge : usdtRecharges) {
                //判断startDate是不是在requestDate之前
                if (startDate.isAfter(usdtRecharge.getRequestDate())) {
                    log.info("USDT充值订单 {} 的请求时间 {} 在检查时间 {} 段前，跳过检查", usdtRecharge.getOrderId(), DateUtil.format(usdtRecharge.getRequestDate(), DatePattern.NORM_DATETIME_PATTERN), DateUtil.format(startDate, DatePattern.NORM_DATETIME_PATTERN));
                    usdtRechargeService.updateStatusAndRemark(usdtRecharge.getId(), 1, "订单超时，请联系管理员检查当前订单");
                    rechargeLogService.updateStatusAndAuditRemark(usdtRecharge.getOrderId(), 1, "订单超时，请联系管理员检查当前订单");
                    continue;
                }
                if (!RedisCustomizeUtils.setIfAbsent("check_usdt_order:" + usdtRecharge.getOrderId())) {
                    log.info("USDT充值订单 {} 正在被其他线程处理，跳过检查", usdtRecharge.getOrderId());
                }
                RechargeLog rechargeLog;
                try {
                    rechargeLog = rechargeLogService.getByOrderId(usdtRecharge.getOrderId());
                } catch (Exception e) {
                    log.error("获取USDT充值订单 {} 的充值记录失败，异常信息: {}", usdtRecharge.getOrderId(), e.getMessage(), e);
                    continue;
                }
                if (rechargeLog == null) {
                    log.error("USDT充值订单 {} 对应的充值记录不存在", usdtRecharge.getOrderId());
                    continue;
                }
                if (rechargeLog.getStatus() != 2) {
                    log.info("USDT充值订单 {} 的充值记录状态不是待审核，同步充值订单状态", usdtRecharge.getOrderId());
                    usdtRechargeService.updateStatusAndRemark(usdtRecharge.getId(), rechargeLog.getStatus(), rechargeLog.getRemark());
                    continue;
                }
                String address = usdtRecharge.getReceiveAddress();
                TronTransactionResponse tronTransactionResponse;
                if (!transferMap.containsKey(address)) {
                    tronTransactionResponse = getTronTransactionResponse(address);
                    if (tronTransactionResponse == null) {
                        log.error("获取USDT充值订单 {} 的交易记录失败", usdtRecharge.getOrderId());
                        continue;
                    }
                    transferMap.put(address, tronTransactionResponse);
                } else {
                    log.info("USDT充值订单 {} 的充值地址 {} 已经获取过交易记录，直接使用缓存", usdtRecharge.getOrderId(), address);
                    tronTransactionResponse = transferMap.get(address);
                }
                List<TokenTransfer> tokenTransfers = tronTransactionResponse.getTokenTransfers();
                if (tokenTransfers == null || tokenTransfers.isEmpty()) {
                    log.info("USDT充值订单 {} 的充值地址 {} 没有交易记录", usdtRecharge.getOrderId(), address);
                    continue;
                }
                //判断当前记录的金额是否在交易记录中,且判断hash是否已经存在
                System.out.println();
                List<TokenTransfer> transferList = tokenTransfers.stream()
                        .filter(it ->
                                new BigDecimal(it.getQuant())
                                        .divide(new BigDecimal("1000000"), 2, RoundingMode.HALF_UP)
                                        .compareTo(usdtRecharge.getRemittanceAmount()) == 0).toList();
                if (transferList.isEmpty()) {
                    log.info("USDT充值订单 {} 的充值地址 {} 充值金额 {} 没有匹配的交易记录", usdtRecharge.getOrderId(), address, usdtRecharge.getRemittanceAmount());
                    continue;
                }
                if (transferList.size() != 1) {
                    log.info("USDT充值订单 {} 的充值地址 {} 有多条交易记录，可能是重复充值，请联系管理员处理", usdtRecharge.getOrderId(), address);
                    usdtRechargeService.updateStatusAndRemark(
                            usdtRecharge.getId(),
                            3,
                            "订单出现相同金额记录，请联系管理员手动处理");
                    rechargeLogService.updateStatusAndAuditRemark(
                            usdtRecharge.getOrderId(),
                            3,
                            "订单出现相同金额记录，请联系管理员手动处理");
                    continue;
                }
                //更新充值订单状态
                TokenTransfer tokenTransfer = transferList.get(0);
                if (usdtRechargeService.existsByTransactionId(tokenTransfer.getTransactionId())) {
                    log.info("USDT充值订单 {} 的充值地址 {} 充值金额 {} 的交易记录 {} 已经存在，跳过处理", usdtRecharge.getOrderId(), address, usdtRecharge.getRemittanceAmount(), tokenTransfer.getTransactionId());
                    usdtRechargeService.updateStatusAndRemark(
                            usdtRecharge.getId(),
                            3,
                            "订单异常，请联系管理员手动处理");
                    rechargeLogService.updateStatusAndAuditRemark(
                            usdtRecharge.getOrderId(),
                            3,
                            "订单异常，请联系管理员手动处理");
                    continue;
                }
                if (tokenTransfer.getBlockTs() < usdtRecharge.getRequestDate().getTime()) {
                    log.info("USDT充值订单 {} 的充值地址 {} 充值金额 {} 的交易记录时间 {} 小于订单请求时间 {}, 跳过处理",
                            usdtRecharge.getOrderId(),
                            address,
                            usdtRecharge.getRemittanceAmount(),
                            DateUtil.format(new DateTime(tokenTransfer.getBlockTs()), DatePattern.NORM_DATETIME_PATTERN),
                            DateUtil.format(usdtRecharge.getRequestDate(), DatePattern.NORM_DATETIME_PATTERN));
                    continue;
                }
                log.info("USDT充值订单 {} 的充值地址 {} 充值金额 {} 匹配到交易记录，更新订单状态", usdtRecharge.getOrderId(), address, usdtRecharge.getRemittanceAmount());
                usdtRechargeService.setFormAddress(
                        usdtRecharge.getId(),
                        0,
                        tokenTransfer.getFromAddress(),
                        tokenTransfer.getTransactionId(),
                        tokenTransfer.getBlock(),
                        tokenTransfer.getBlockTs(),
                        JSONUtil.toJsonStr(tokenTransfer)
                );
                //更新充值记录状态
                rechargeLogService.autoAudit(
                        AuditOrderDto.builder()
                                .id(rechargeLog.getId())
                                .voucher(tokenTransfer.getTransactionId())
                                .status(true)
                                .remark("自动入账")
                                .remittanceAddress(tokenTransfer.getFromAddress())
                                .build()
                );
                RedisCustomizeUtils.removeKey("check_usdt_order:" + usdtRecharge.getOrderId());
            }
        } catch (Exception e) {
            log.error("检查USDT充值订单状态异常: {}", e.getMessage(), e);
        } finally {
            RedisCustomizeUtils.removeKey("check:usdt");
        }
    }

    public TronTransactionResponse getTronTransactionResponse(String address) {
        // 这里应该调用实际的服务来获取 TronTransactionResponse
        // 目前返回一个空的 TronTransactionResponse 作为示例

        HashMap<String, Object> param = HttpUtil.createParam();
        param.put("contractAddress", usdtConfig.getContractAddress());
        param.put("toAddress", address);
        param.put("relatedAddress", address);
        param.put("limit", 50);
        param.put("start", 0);
        param.put("sort", -1);
        param.put("count", true);
        //查询最近30分钟的数据 毫秒级

        param.put("start_timestamp", DateUtil.offsetMinute(new DateTime(), -30).getTime());
        param.put("filterTokenValue", 0);
        HashMap<String, String> header = HttpUtil.createHeader();
        header.put("TRON-PRO-API-KEY", usdtConfig.getApiKey());
        try {
            String result = HttpUtil.get(
                    usdtConfig.getUrl(),
                    param,
                    header
            );
            return JSONUtil.toBean(result, TronTransactionResponse.class);
        } catch (Exception e) {
            log.error("获取USDT地址：{}, 充值交易记录失败,失败原因: {}", address, e.getMessage(), e);
            return null;
        }

    }


    //每天凌晨3点执行
    @Scheduled(cron = "0 0 3 * * ?")
    public void getLlBillTransaction() {
        DateTime dateTime = DateUtil.offsetDay(new DateTime(), -1);
        DateTime startDate = DateUtil.beginOfDay(dateTime);
        DateTime endDate = DateUtil.endOfDay(dateTime);
        log.info("开始获取链上账单，时间范围：{} - {}", DateUtil.format(startDate, DatePattern.NORM_DATETIME_PATTERN), DateUtil.format(endDate, DatePattern.NORM_DATETIME_PATTERN));
        PageVo<PageClearingVo> paginged;
        int pageIndex = 1;
        do {
            paginged = llService.pagingClearing(
                    pageIndex++,
                    200,
                    null,
                    null,
                    null,
                    startDate,
                    endDate);
            if (paginged == null || paginged.getData() == null || paginged.getData().isEmpty()) {
                log.info("没有更多的链上账单数据，结束获取");
                break;
            }
            for (PageClearingVo clearingVo : paginged.getData()) {
                switch (clearingVo.getTransactionType()) {
                    case "SETTLE" -> {
                        if (!transactionService.existsByChannelTranId(clearingVo.getOriginalTransactionId())) {
                            log.info("链上账单交易ID：{} 已经存在，跳过处理", clearingVo.getOriginalTransactionId());
                            continue;
                        }
                        String tranId = transactionService.getTranIdByChannelTranId(clearingVo.getOriginalTransactionId());

                        transactionService.updateTranSettleData(
                                clearingVo.getOriginalTransactionId(),
                                clearingVo.getTransactionId(),
                                tranId,
                                "SUCCESS",
                                clearingVo.getBillingAmount(),
                                clearingVo.getBillingCurrency(),
                                clearingVo.getClearingDate(),
                                new Date(),
                                true
                        );
                    }
                    case "SETTLE_REVERSE" -> {
                        String tranId = transactionService.getTranIdByChannelTranId(clearingVo.getOriginalTransactionId());
                        UserCard userCard = userCardService.getByChannelCardId(clearingVo.getChannelCardId());
                        Transaction transaction = Transaction.builder()
                                .tranId(IdUtil.getSnowflakeNextIdStr())
                                .channelCardId(clearingVo.getChannelCardId())
                                .channelTranId(clearingVo.getTransactionId())
                                .originalTranId(tranId)
                                .originalChannelTranId(clearingVo.getOriginalTransactionId())
                                .channel("LL")
                                .cardNumber(userCard.getCardNumber())
                                .userCardId(userCard.getId())
                                .userId(userCard.getUserId())
                                .cardTypeId(userCard.getCardTypeId())
                                .merchantName(clearingVo.getMerchantName())
                                .tranDate(clearingVo.getClearingDate())
                                .merchantAmount(clearingVo.getTransactionAmount())
                                .merchantCurrency(clearingVo.getTransactionCurrency())
                                .billingAmount(clearingVo.getBillingAmount())
                                .billingCurrency(clearingVo.getBillingCurrency())
                                .settleAmount(clearingVo.getBillingAmount())
                                .settleCurrency(clearingVo.getBillingCurrency())
                                .settleDate(clearingVo.getClearingDate())
                                .postDate(null)
                                .hasClear(true)
                                .transactionType("REFUND")
                                .status("SUCCESS")
                                .code("")
                                .remark("")
                                .mcc(clearingVo.getMcc())
                                .fee(BigDecimal.ZERO)
                                .feeCurrency("")
                                .feeInfo("")
                                .merchantCountry(clearingVo.getMerchantCountry())
                                .cardType(userCard.getCardType())
                                .merchantCity("")
                                .build();
                        transactionService.save(transaction);
                        log.info("处理链上账单，交易类型：{}，交易ID：{}，原交易ID：{}", clearingVo.getTransactionType(), clearingVo.getTransactionId(), clearingVo.getOriginalTransactionId());
                        refund(
                                clearingVo.getTransactionId(),
                                clearingVo.getOriginalTransactionId(),
                                userCard,
                                cardTypeService.getById(userCard.getCardTypeId()),
                                userCardWalletService.findByUserIdAndCardId(userCard.getUserId(), userCard.getCardTypeId()),
                                clearingVo.getBillingAmount()
                        );
                    }
                    default -> {
                        log.info("处理链上账单，交易类型：{}，交易ID：{}", clearingVo.getTransactionType(), clearingVo.getTransactionId());
                    }
                }
            }
        } while (paginged.getHasMore());
    }

    private void refund(
            String transactionId,
            String accountTransactionId,
            UserCard userCard,
            CardType cardType,
            UserCardWallet userCardWallet,
            BigDecimal billingAmount
    ) {
        log.info("当前处理交易:{}, 用户卡ID:{}, 卡类型:{}, 交易金额:{}", transactionId, userCard.getId(), cardType.getCode(), billingAmount);
        log.info("当前号段: {}, 卡号: {}, 退款手续费基数: {}, 比例: {}, ", cardType.getSectionNo(), userCard.getCardNumber(),
                userCardWallet.getRefundFee(), userCardWallet.getRefundFeeRate()
        );
        Wallet wallet = walletService.getByUserIdAndCurrencyCode(userCard.getUserId(), userCard.getCurrencyCode());
        //退款手续费
        BigDecimal refundFee = billingAmount
                .multiply(userCardWallet.getRefundFeeRate().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP))
                .add(userCardWallet.getRefundFee()).setScale(2, RoundingMode.HALF_UP);
        if (refundFee.compareTo(BigDecimal.ZERO) > 0) {
            log.info("交易成功，扣除退款手续费: {}", refundFee);
            BigDecimal balance = walletService.subtractUsableQuotaById(
                    wallet.getId(),
                    refundFee,
                    1,
                    true
            );
            walletDetailService.refundFee(
                    userCard.getId(),
                    cardType.getId(),
                    userCard.getChannelCode(),
                    userCard.getCardNumber(),
                    accountTransactionId,
                    "",
                    refundFee,
                    balance,
                    wallet.getCurrencyId(),
                    userCard.getUserId()
            );
        }

    }


    //每天下午4点统计点执行
    @Scheduled(cron = "0 0 16 * * ?")
//    @Scheduled(cron = "0 5 * * * ?")
    public void calculateAccount() {
        RedisCustomizeUtils.set("calculate:account", "1");
        try {
            String day = DateUtil.format(DateUtil.offsetDay(new Date(), -1), DatePattern.NORM_DATE_PATTERN);
//        walletDetailService
            List<JSONObject> userStatistic = userService.statistics();
            List<JSONObject> accountRecharge = rechargeLogService.statistics();
            List<JSONObject> accountWithdraw = withdrawService.statistics();
            List<JSONObject> cardOrderRechargeAndWithdraw = cardOrderService.statistics();
            List<JSONObject> statistics = transactionService.statistics();
            Set<Long> userIdSet = new HashSet<>();
            //把所有的用户ID放到一个Set中
            userIdSet.addAll(
                    userStatistic.stream().map(it -> it.getLong("userId")).distinct().toList()
            );
            userIdSet.addAll(
                    accountRecharge.stream().map(it -> it.getLong("userId")).distinct().toList()
            );
            userIdSet.addAll(
                    accountWithdraw.stream().map(it -> it.getLong("userId")).distinct().toList()
            );
            userIdSet.addAll(
                    cardOrderRechargeAndWithdraw.stream().map(it -> it.getLong("userId")).distinct().toList()
            );
            userIdSet.addAll(
                    statistics.stream().map(it -> it.getLong("userId")).distinct().toList()
            );
            for (Long userId : userIdSet) {
                //充值总额
                JSONObject user = userStatistic.stream()
                        .filter(it -> it.getLong("userId").equals(userId))
                        .findFirst()
                        .orElse(null);
                JSONObject recharge = accountRecharge.stream()
                        .filter(it -> it.getLong("userId").equals(userId))
                        .findFirst()
                        .orElse(null);
                JSONObject withdraw = accountWithdraw.stream()
                        .filter(it -> it.getLong("userId").equals(userId))
                        .findFirst()
                        .orElse(null);
                JSONObject cardOrder = cardOrderRechargeAndWithdraw.stream()
                        .filter(it -> it.getLong("userId").equals(userId))
                        .findFirst()
                        .orElse(null);
                JSONObject stat = statistics.stream()
                        .filter(it -> it.getLong("userId").equals(userId))
                        .findFirst()
                        .orElse(null);
                UserStatistics userStatistics = UserStatistics.builder()
                        .userId(userId)
                        .date(day)
                        .accountRechargeAmount(recharge == null ? BigDecimal.ZERO : recharge.getBigDecimal("successAmount"))
                        .accountRechargeCount(recharge == null ? 0 : recharge.getIntValue("successCount"))
                        .accountWithdrawAmount(withdraw == null ? BigDecimal.ZERO : withdraw.getBigDecimal("successAmount"))
                        .accountWithdrawCount(withdraw == null ? 0 : withdraw.getIntValue("successCount"))
                        .cardRechargeAmount(cardOrder == null ? BigDecimal.ZERO : cardOrder.getBigDecimal("rechargeAmount"))
                        .cardRechargeCount(cardOrder == null ? 0 : cardOrder.getIntValue("rechargeCount"))
                        .cardWithdrawAmount(cardOrder == null ? BigDecimal.ZERO : cardOrder.getBigDecimal("withdrawAmount"))
                        .cardWithdrawCount(cardOrder == null ? 0 : cardOrder.getIntValue("withdrawCount"))
                        .cardConsumeAmount(stat == null ? BigDecimal.ZERO : stat.getBigDecimal("sumAmount"))
                        .cardConsumeSuccessAmount(stat == null ? BigDecimal.ZERO : stat.getBigDecimal("successAuthAmount"))
                        .cardConsumeFailAmount(stat == null ? BigDecimal.ZERO : stat.getBigDecimal("failedAuthAmount"))
                        .cardConsumeCount(stat == null ? 0 : stat.getIntValue("sumCount"))
                        .cardConsumeSuccessCount(stat == null ? 0 : stat.getIntValue("successAuthCount"))
                        .cardConsumeFailCount(stat == null ? 0 : stat.getIntValue("failedAuthCount"))
                        .cardRefundAmount(stat == null ? BigDecimal.ZERO : stat.getBigDecimal("refundAmount"))
                        .cardRefundCount(stat == null ? 0 : stat.getIntValue("refundCount"))
                        .cardReversalAmount(stat == null ? BigDecimal.ZERO : stat.getBigDecimal("reversalAmount"))
                        .cardReversalCount(stat == null ? 0 : stat.getIntValue("reversalCount"))
                        .verifyCount(stat == null ? 0 : stat.getIntValue("verifyCount"))
                        .openCardCount(cardOrder == null ? 0 : cardOrder.getIntValue("openCount"))
                        .closeCardCount(cardOrder == null ? 0 : cardOrder.getIntValue("closeCount"))
                        .registerCount(user == null ? 0 : user.getIntValue("registerCount"))
                        .build();
                userStatisticsService.save(userStatistics);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            RedisCustomizeUtils.removeKey("calculate:account");
        }
    }


    /**
     * 30分钟一次
     */
    @Scheduled(cron = "0 0/30 * * * ?")
    public void riskUserCardFun() {
        //获取最近半个小时的交易数据
        DateTime endDate = DateUtil.date();
        DateTime startDate = DateUtil.offsetMinute(endDate, -30);
        log.info("风控用户交易数据检查，时间范围：{} - {}", DateUtil.format(startDate, DatePattern.NORM_DATETIME_PATTERN), DateUtil.format(endDate, DatePattern.NORM_DATETIME_PATTERN));
        List<Long> userCardIds = transactionService.listUserCardIdByDate(startDate, endDate);
        if (userCardIds.isEmpty()) {
            log.info("没有需要检查的用户交易数据");
            return;
        }
        for (Long userCardId : userCardIds) {
            List<Risk> risks = riskService.list();
            UserCard userCard = userCardService.getByIdOpt(userCardId).orElse(null);
            if (userCard == null) {
                log.warn("当前用户卡不存在,用户卡ID:{}", userCardId);
                return;
            }
            log.info("当前交易需要检查风控,用户卡Id:{}", userCardId);
            List<Risk> baseRisk = risks.stream().filter(it -> it.getCode().equals("TRANSACTION") && it.getStatus() == 0 && it.getTargetId() == 0L).toList();
            List<Risk> cardRisk = risks.stream().filter(it -> it.getCode().equals("TRANSACTION") && it.getStatus() == 0 && it.getTargetId().equals(userCard.getCardTypeId())).toList();
            List<Risk> userRisk = risks.stream().filter(it -> it.getCode().equals("TRANSACTION") && it.getStatus() == 0 && it.getTargetId().equals(userCard.getUserId())).toList();
            log.info("当前交易需要风控,用户卡Id:{},公共规则数量:{},用户规则数量:{},卡类型规则数量:{}", userCardId, baseRisk.size(), userRisk.size(), cardRisk.size());
            // 获取这三个集合中最大的时间
            // 优先级 公共 > 用户 > 卡类型
            for (Risk risk : baseRisk) {
                Map<String, Object> resultMap = checkRisk(risk, userCard);
                if ((Boolean) resultMap.get("result")) {
                    log.info("当前交易触发公共风控,卡号:{},风控规则ID:{}", userCard.getCardNumber(), risk.getId());
                    RiskUserCardLog riskTransactionLog = RiskUserCardLog.builder()
                            .riskId(risk.getId())
                            .triggerEventDate(new Date())
                            .status(2)
                            .disposeStatus(1)
                            .disposeObject(risk.getDisposeObject())
                            .disposeFunctionName(risk.getFunctionName())
                            .hasAuto(risk.getHasAuto())
                            .remark("")
                            .balance(getCardBalance(userCard.getChannelCode(), userCard.getChannelCardId()))
                            .userId(userCard.getUserId())
                            .cardTypeId(userCard.getCardTypeId())
                            .userCardId(userCard.getId())
                            .cardNumber(userCard.getCardNumber())
                            .channelName(userCard.getChannelCode())
                            .key(resultMap.get("key").toString())
                            .practicalRate((BigDecimal) resultMap.get("rate"))
                            .riskRate((BigDecimal) resultMap.get("riskRate"))
                            .build();
                    riskUserCardLogService.save(riskTransactionLog);
                    // 触发风控
                    if (risk.getHasAuto()) {
                        try {
                            boolean disposed = disposeCard(
                                    userCard.getId(),
                                    userCard.getCardTypeId(),
                                    userCard.getChannelCardId(),
                                    userCard.getUserId(),
                                    userCard.getCardNumber(),
                                    userCard.getChannelCode(),
                                    risk.getFunctionName()
                            );
                            if (disposed) {
                                // 如果公共风控已经处理了，则不再处理后续的风控
                                log.info("当前交易公共风控已处理,卡号:{},风控规则ID:{}", userCard.getCardNumber(), risk.getId());
                                riskUserCardLogService.completeTransactionLog(
                                        riskTransactionLog.getId(),
                                        0,
                                        "自动处理成功"
                                );

                            }
                        } catch (Exception e) {
                            riskUserCardLogService.completeTransactionLog(
                                    riskTransactionLog.getId(),
                                    1,
                                    "自动处理失败" + e.getMessage()
                            );
                        }
                    } else {
                        log.info("当前交易公共风控未自动处理,卡号:{},风控规则ID:{}", userCard.getCardNumber(), risk.getId());
                        riskUserCardLogService.completeTransactionLog(
                                riskTransactionLog.getId(),
                                2,
                                "触发风控，需要手动处理"
                        );
                    }
                    return;
                }
            }
            for (Risk risk : userRisk) {
                Map<String, Object> resultMap = checkRisk(risk, userCard);
                if ((Boolean) resultMap.get("result")) {
                    log.info("当前交易触发用户风控,卡号:{},风控规则ID:{}", userCard.getCardNumber(), risk.getId());
                    // 触发风控
                    RiskUserCardLog riskTransactionLog = RiskUserCardLog.builder()
                            .riskId(risk.getId())
                            .triggerEventDate(new Date())
                            .status(2)
                            .disposeStatus(1)
                            .disposeObject(risk.getDisposeObject())
                            .disposeFunctionName(risk.getFunctionName())
                            .hasAuto(risk.getHasAuto())
                            .remark("")
                            .balance(getCardBalance(userCard.getChannelCode(), userCard.getChannelCardId()))
                            .userId(userCard.getUserId())
                            .cardTypeId(userCard.getCardTypeId())
                            .userCardId(userCard.getId())
                            .cardNumber(userCard.getCardNumber())
                            .channelName(userCard.getChannelCode())
                            .key(resultMap.get("key").toString())
                            .practicalRate((BigDecimal) resultMap.get("rate"))
                            .riskRate((BigDecimal) resultMap.get("riskRate"))
                            .build();
                    riskUserCardLogService.save(riskTransactionLog);
                    if (risk.getHasAuto()) {
                        try {
                            disposeUser(
                                    userCard.getUserId(),
                                    "block"
                            );
                            boolean disposed = disposeCard(
                                    userCard.getId(),
                                    userCard.getCardTypeId(),
                                    userCard.getChannelCardId(),
                                    userCard.getUserId(),
                                    userCard.getCardNumber(),
                                    userCard.getChannelCode(),
                                    risk.getFunctionName()
                            );
                            if (disposed) {
                                // 如果公共风控已经处理了，则不再处理后续的风控
                                log.info("当前交易用户风控已处理,卡号:{},风控规则ID:{}", userCard.getCardNumber(), risk.getId());
                                riskUserCardLogService.completeTransactionLog(
                                        riskTransactionLog.getId(),
                                        0,
                                        "自动处理成功"
                                );
                                return;
                            }
                        } catch (Exception e) {
                            riskUserCardLogService.completeTransactionLog(
                                    riskTransactionLog.getId(),
                                    1,
                                    "自动处理失败" + e.getMessage()
                            );
                        }
                    } else {
                        log.info("当前交易用户风控未自动处理,卡号:{},风控规则ID:{}", userCard.getCardNumber(), risk.getId());
                        riskUserCardLogService.completeTransactionLog(
                                riskTransactionLog.getId(),
                                2,
                                "触发风控，需要手动处理"
                        );
                    }
                    return;
                }
            }
            for (Risk risk : cardRisk) {
                Map<String, Object> resultMap = checkRisk(risk, userCard);
                if ((Boolean) resultMap.get("result")) {
                    log.info("当前交易触发卡类型风控,卡号:{},风控规则ID:{}", userCard.getCardNumber(), risk.getId());
                    // 触发风控
                    RiskUserCardLog riskTransactionLog = RiskUserCardLog.builder()
                            .riskId(risk.getId())
                            .triggerEventDate(new Date())
                            .status(2)
                            .disposeStatus(1)
                            .disposeObject(risk.getDisposeObject())
                            .disposeFunctionName(risk.getFunctionName())
                            .hasAuto(risk.getHasAuto())
                            .remark("")
                            .balance(getCardBalance(userCard.getChannelCode(), userCard.getChannelCardId()))
                            .userId(userCard.getUserId())
                            .cardTypeId(userCard.getCardTypeId())
                            .userCardId(userCard.getId())
                            .cardNumber(userCard.getCardNumber())
                            .channelName(userCard.getChannelCode())
                            .key(resultMap.get("key").toString())
                            .practicalRate((BigDecimal) resultMap.get("rate"))
                            .riskRate((BigDecimal) resultMap.get("riskRate"))
                            .build();
                    riskUserCardLogService.save(riskTransactionLog);
                    if (risk.getHasAuto()) {
                        try {
                            boolean disposed = disposeCard(
                                    userCard.getId(),
                                    userCard.getCardTypeId(),
                                    userCard.getChannelCardId(),
                                    userCard.getUserId(),
                                    userCard.getCardNumber(),
                                    userCard.getChannelCode(),
                                    risk.getFunctionName()
                            );
                            if (disposed) {
                                // 如果公共风控已经处理了，则不再处理后续的风控
                                log.info("当前交易卡类型风控已处理,卡号:{},风控规则ID:{}", userCard.getCardNumber(), risk.getId());
                                riskUserCardLogService.completeTransactionLog(
                                        riskTransactionLog.getId(),
                                        0,
                                        "自动处理成功"
                                );
                            }
                        } catch (Exception e) {
                            riskUserCardLogService.completeTransactionLog(
                                    riskTransactionLog.getId(),
                                    1,
                                    "自动处理失败" + e.getMessage()
                            );
                        }
                    } else {
                        log.info("当前交易卡类型风控未自动处理,卡号:{},风控规则ID:{}", userCard.getCardNumber(), risk.getId());
                        riskUserCardLogService.completeTransactionLog(
                                riskTransactionLog.getId(),
                                2,
                                "触发风控，需要手动处理"
                        );
                    }
                    return;
                }
            }
        }


    }

    private boolean disposeCard(Long userCardId, Long cardId, String channelCardId, Long userId, String cardNumber, String channelName, String disposeFunc) {
        ChannelBaseService baseService = applicationContext.getBean(channelName, ChannelBaseService.class);
        String requestId = IdUtil.getSnowflakeNextIdStr();
        String cardStatus = baseService.queryCardStatus(channelCardId);
        if (disposeFunc.equals("block")) {
            if (cardStatus.equals(CardStatusEnum.ACTIVE.getCode())) {
                cardOrderService.blockCard(
                        requestId,
                        cardId,
                        userId,
                        0L,
                        cardNumber,
                        userCardId,
                        channelCardId,
                        "系统风控冻结卡",
                        "SYSTEM"
                );
                baseService.blockCard(channelCardId, requestId);
            }
            if (cardStatus.equals(CardStatusEnum.ACTIVE.getCode()) || cardStatus.equals(CardStatusEnum.BLOCKED.getCode())) {
                userCardService.updateCardStatusByChannelCardId(
                        CardStatusEnum.CONTROL.getCode(),
                        channelCardId
                );
            } else {
                log.info("当前卡状态不可冻结,渠道:{},卡ID:{},状态:{}", channelName, channelCardId, cardStatus);
            }
        } else {
            if (cardStatus.equals(CardStatusEnum.ACTIVE.getCode())) {
                cardOrderService.closeCard(
                        requestId,
                        cardId,
                        userId,
                        0L,
                        cardNumber,
                        userCardId,
                        channelCardId,
                        "系统风控销卡",
                        "CLIENT"
                );
                baseService.cancelCard(
                        CardCancelDto.builder()
                                .channelCardId(channelCardId)
                                .requestId(requestId)
                                .build()
                );
            }
            if (cardStatus.equals(CardStatusEnum.BLOCKED.getCode())) {
                userCardService.updateCardStatusByChannelCardId(
                        CardStatusEnum.CONTROL.getCode(),
                        channelCardId
                );
            }
        }
        return true;
    }

    private boolean disposeUser(Long userId, String disposeFunc) {
        // TODO 这里后续完善用户的风控
        if (disposeFunc.equals("block")) {
            userService.updateStatusAndAdminRemarkById(userId, 2, "用户风控冻结");
            log.info("用户风控冻结:{}", userId);
        } else {
            log.info("用户风控销户:{}", userId);
        }
        return true;
    }

    private BigDecimal getCardBalance(String channelName, String channelCardId) {
        ChannelBaseService baseService = applicationContext.getBean(channelName, ChannelBaseService.class);
        try {
            return baseService.queryCardBalance(channelCardId);
        } catch (Exception e) {
            log.error("获取卡余额失败,渠道:{},卡ID:{},错误信息:{}", channelName, channelCardId, e.getMessage());
            return BigDecimal.ZERO;
        }
    }

    private Map<String, Object> checkRisk(Risk risk, UserCard userCard) {
        log.info("当前风控ID:{},风控名称:{},风控类型:{}", risk.getId(), risk.getName(), risk.getRuleType() ? "单次交易次数风控" : "交易统计风控");
        if (risk.getRuleType()) {
            return Map.of(
                    "result", false
            );
        }
        try {
            List<RiskRule> riskRules = riskRuleService.listByRiskId(risk.getId());
            for (RiskRule riskRule : riskRules.stream().filter(it -> it.getRiskId().equals(risk.getId())).toList()) {
                log.info("当前检查的风控ID:{},卡号:{}", risk.getId(), userCard.getCardNumber());
                //计算开始时间和结束时间根据风控规则的day往前推
                if (StringUtils.isNotBlank(riskRule.getDay()) && NumberUtil.isNumber(riskRule.getDay())) {
                    DateTime startDate = DateUtil.beginOfDay(DateUtil.offsetDay(new Date(), -Integer.parseInt(riskRule.getDay())));
                    DateTime endDate = DateUtil.endOfDay(new Date());
                    JSONObject statistics = transactionService.userCardStatistics(startDate, endDate, userCard.getId());
                    Integer sumCount = statistics.getIntValue("sumCount");
                    if (sumCount < risk.getMinTransactionCount() || sumCount < 1) {
                        log.info("当前风控未达到最小交易次数,风控ID:{},最小交易次数:{},实际交易次数:{}", risk.getId(), risk.getMinTransactionCount(), sumCount);
                        return Map.of(
                                "result", false
                        );
                    }
                    Integer successAuthCount = statistics.getIntValue("successAuthCount");
                    Integer failedAuthCount = statistics.getIntValue("failedAuthCount");
                    Integer authCount = statistics.getIntValue("authCount");
                    Integer refundCount = statistics.getIntValue("refundCount");
                    Integer reversalCount = statistics.getIntValue("reversalCount");
                    Integer verifyCount = statistics.getIntValue("verifyCount");
                    // >  >=

                    // 多个条件是与的关系 同时满足 才触发风控 只要都没触发 就返回false

                    if (riskRule.getKey().equals("fail") && StringUtils.isNotBlank(riskRule.getValue())) {
                        //计算失败率  失败数 / 总数 * 100
                        BigDecimal failRate = BigDecimal.valueOf(failedAuthCount).divide(BigDecimal.valueOf(authCount), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
                        if (checkValue(riskRule.getFun(), new BigDecimal(riskRule.getValue()), failRate)) {
                            return Map.of(
                                    "result", true,
                                    "key", "fail",
                                    "rate", failRate,
                                    "riskRate", new BigDecimal(riskRule.getValue())
                            );
                        }
                    }
                    if (riskRule.getKey().equals("refund") && StringUtils.isNotBlank(riskRule.getValue())) {
                        BigDecimal refundRate = BigDecimal.valueOf(refundCount).divide(BigDecimal.valueOf(successAuthCount), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
                        if (checkValue(riskRule.getFun(), new BigDecimal(riskRule.getValue()), refundRate)) {
                            return Map.of(
                                    "result", true,
                                    "key", "refund",
                                    "rate", refundRate,
                                    "riskRate", new BigDecimal(riskRule.getValue())
                            );
                        }
                    }
                    if (riskRule.getKey().equals("reversal") && StringUtils.isNotBlank(riskRule.getValue())) {
                        BigDecimal reversalRate = BigDecimal.valueOf(reversalCount).divide(BigDecimal.valueOf(successAuthCount), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
                        if (checkValue(riskRule.getFun(), new BigDecimal(riskRule.getValue()), reversalRate)) {
                            return Map.of(
                                    "result", true,
                                    "key", "reversal",
                                    "rate", reversalRate,
                                    "riskRate", new BigDecimal(riskRule.getValue())
                            );
                        }
                    }
                    if (riskRule.getKey().equals("verify") && StringUtils.isNotBlank(riskRule.getValue())) {
                        BigDecimal verifyRate = BigDecimal.valueOf(verifyCount).divide(BigDecimal.valueOf(sumCount), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
                        if (checkValue(riskRule.getFun(), new BigDecimal(riskRule.getValue()), verifyRate)) {
                            return Map.of(
                                    "result", true,
                                    "key", "verify",
                                    "rate", verifyRate,
                                    "riskRate", new BigDecimal(riskRule.getValue())
                            );
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("风控规则计算异常,风控ID:{},错误信息:{}", risk.getId(), e.getMessage());
            return Map.of(
                    "result", false
            );
        }
        return Map.of(
                "result", false
        );
    }


    private boolean checkValue(String condition, BigDecimal conditionValue, BigDecimal practicalValue) {
        boolean flag = switch (condition) {
            case ">" -> practicalValue.compareTo(conditionValue) > 0;
            case ">=" -> practicalValue.compareTo(conditionValue) > -1;
            default -> true;
        };
        log.info("风控检查条件:{},风控值:{},交易值:{},风控检查结果:{}", condition, conditionValue, practicalValue, flag);
        return flag;
    }
}